package cn.zwx.mvcframework.aop;

import cn.zwx.mvcframework.aop.advice.ZwxAdvice;
import cn.zwx.mvcframework.aop.support.ZwxAdvisedSupport;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;
import java.util.Map;

/**
 * @author zhangwenxue
 */
public class ZwxCgliDynamicAopProxy implements ZwxAopPoxy, MethodInterceptor {

    private ZwxAdvisedSupport advisedSupport;


    public ZwxCgliDynamicAopProxy(ZwxAdvisedSupport advisedSupport) {
        this.advisedSupport = advisedSupport;
    }

    @Override
    public Object getProxy() {
        return getProxy(this.getClass().getClassLoader());
    }

    @Override
    public Object getProxy(ClassLoader classLoader) {
        // Configure CGLIB Enhancer...
        Enhancer enhancer = new Enhancer();
        // 设置类加载器
        enhancer.setClassLoader(classLoader);
        enhancer.setSuperclass(advisedSupport.getTargetClass());
        enhancer.setInterfaces(advisedSupport.getTargetClass().getInterfaces());
        enhancer.setCallback(this);
        // Generate the proxy class and create a proxy instance.
        return enhancer.create();
    }


    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        // 判断方法是否有对应的执行器链
        Map<String, ZwxAdvice> advices = advisedSupport.getAdvices(method);
        Object returnValue;
        try {
            this.invokeAdvice(advices.get("before"));
            returnValue = method.invoke(this.advisedSupport.getTarget(),objects);
            this.invokeAdvice(advices.get("after"));
        }catch (Exception e){
            this.invokeAdvice(advices.get("afterThrow"));
            throw e;
        }
        return returnValue;
    }

    /**
     * 执行切入点方法
     * @param advice 切入点
     */
    private void invokeAdvice(ZwxAdvice advice) {
        try {
            advice.getAdviceMethod().invoke(advice.getAspect(),advice.getAdviceMethod());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
